// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/threading/sequenced_worker_pool.h"

#include <stdint.h>

#include <list>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>

#include "base/atomic_sequence_num.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/critical_closure.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/heap_profiler.h"
#include "base/trace_event/trace_event.h"
#include "base/tracked_objects.h"
#include "build/build_config.h"

#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
#elif defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif

#if !defined(OS_NACL)
#include "base/metrics/histogram.h"
#endif

namespace base {

namespace {

    struct SequencedTask : public TrackingInfo {
        SequencedTask()
            : sequence_token_id(0)
            , trace_id(0)
            , sequence_task_number(0)
            , shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN)
        {
        }

        explicit SequencedTask(const tracked_objects::Location& from_here)
            : base::TrackingInfo(from_here, TimeTicks())
            , sequence_token_id(0)
            , trace_id(0)
            , sequence_task_number(0)
            , shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN)
        {
        }

        ~SequencedTask() { }

        int sequence_token_id;
        int trace_id;
        int64_t sequence_task_number;
        SequencedWorkerPool::WorkerShutdown shutdown_behavior;
        tracked_objects::Location posted_from;
        Closure task;

        // Non-delayed tasks and delayed tasks are managed together by time-to-run
        // order. We calculate the time by adding the posted time and the given delay.
        TimeTicks time_to_run;
    };

    struct SequencedTaskLessThan {
    public:
        bool operator()(const SequencedTask& lhs, const SequencedTask& rhs) const
        {
            if (lhs.time_to_run < rhs.time_to_run)
                return true;

            if (lhs.time_to_run > rhs.time_to_run)
                return false;

            // If the time happen to match, then we use the sequence number to decide.
            return lhs.sequence_task_number < rhs.sequence_task_number;
        }
    };

    // SequencedWorkerPoolTaskRunner ---------------------------------------------
    // A TaskRunner which posts tasks to a SequencedWorkerPool with a
    // fixed ShutdownBehavior.
    //
    // Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
    class SequencedWorkerPoolTaskRunner : public TaskRunner {
    public:
        SequencedWorkerPoolTaskRunner(
            scoped_refptr<SequencedWorkerPool> pool,
            SequencedWorkerPool::WorkerShutdown shutdown_behavior);

        // TaskRunner implementation
        bool PostDelayedTask(const tracked_objects::Location& from_here,
            const Closure& task,
            TimeDelta delay) override;
        bool RunsTasksOnCurrentThread() const override;

    private:
        ~SequencedWorkerPoolTaskRunner() override;

        const scoped_refptr<SequencedWorkerPool> pool_;

        const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;

        DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolTaskRunner);
    };

    SequencedWorkerPoolTaskRunner::SequencedWorkerPoolTaskRunner(
        scoped_refptr<SequencedWorkerPool> pool,
        SequencedWorkerPool::WorkerShutdown shutdown_behavior)
        : pool_(std::move(pool))
        , shutdown_behavior_(shutdown_behavior)
    {
    }

    SequencedWorkerPoolTaskRunner::~SequencedWorkerPoolTaskRunner()
    {
    }

    bool SequencedWorkerPoolTaskRunner::PostDelayedTask(
        const tracked_objects::Location& from_here,
        const Closure& task,
        TimeDelta delay)
    {
        if (delay.is_zero()) {
            return pool_->PostWorkerTaskWithShutdownBehavior(
                from_here, task, shutdown_behavior_);
        }
        return pool_->PostDelayedWorkerTask(from_here, task, delay);
    }

    bool SequencedWorkerPoolTaskRunner::RunsTasksOnCurrentThread() const
    {
        return pool_->RunsTasksOnCurrentThread();
    }

    // SequencedWorkerPoolSequencedTaskRunner ------------------------------------
    // A SequencedTaskRunner which posts tasks to a SequencedWorkerPool with a
    // fixed sequence token.
    //
    // Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
    class SequencedWorkerPoolSequencedTaskRunner : public SequencedTaskRunner {
    public:
        SequencedWorkerPoolSequencedTaskRunner(
            scoped_refptr<SequencedWorkerPool> pool,
            SequencedWorkerPool::SequenceToken token,
            SequencedWorkerPool::WorkerShutdown shutdown_behavior);

        // TaskRunner implementation
        bool PostDelayedTask(const tracked_objects::Location& from_here,
            const Closure& task,
            TimeDelta delay) override;
        bool RunsTasksOnCurrentThread() const override;

        // SequencedTaskRunner implementation
        bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
            const Closure& task,
            TimeDelta delay) override;

    private:
        ~SequencedWorkerPoolSequencedTaskRunner() override;

        const scoped_refptr<SequencedWorkerPool> pool_;

        const SequencedWorkerPool::SequenceToken token_;

        const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;

        DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolSequencedTaskRunner);
    };

    SequencedWorkerPoolSequencedTaskRunner::SequencedWorkerPoolSequencedTaskRunner(
        scoped_refptr<SequencedWorkerPool> pool,
        SequencedWorkerPool::SequenceToken token,
        SequencedWorkerPool::WorkerShutdown shutdown_behavior)
        : pool_(std::move(pool))
        , token_(token)
        , shutdown_behavior_(shutdown_behavior)
    {
    }

    SequencedWorkerPoolSequencedTaskRunner::
        ~SequencedWorkerPoolSequencedTaskRunner()
    {
    }

    bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTask(
        const tracked_objects::Location& from_here,
        const Closure& task,
        TimeDelta delay)
    {
        if (delay.is_zero()) {
            return pool_->PostSequencedWorkerTaskWithShutdownBehavior(
                token_, from_here, task, shutdown_behavior_);
        }
        return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
    }

    bool SequencedWorkerPoolSequencedTaskRunner::RunsTasksOnCurrentThread() const
    {
        return pool_->IsRunningSequenceOnCurrentThread(token_);
    }

    bool SequencedWorkerPoolSequencedTaskRunner::PostNonNestableDelayedTask(
        const tracked_objects::Location& from_here,
        const Closure& task,
        TimeDelta delay)
    {
        // There's no way to run nested tasks, so simply forward to
        // PostDelayedTask.
        return PostDelayedTask(from_here, task, delay);
    }

    // Create a process-wide unique ID to represent this task in trace events. This
    // will be mangled with a Process ID hash to reduce the likelyhood of colliding
    // with MessageLoop pointers on other processes.
    uint64_t GetTaskTraceID(const SequencedTask& task, void* pool)
    {
        return (static_cast<uint64_t>(task.trace_id) << 32) | static_cast<uint64_t>(reinterpret_cast<intptr_t>(pool));
    }

} // namespace

// Worker ---------------------------------------------------------------------

class SequencedWorkerPool::Worker : public SimpleThread {
public:
    // Hold a (cyclic) ref to |worker_pool|, since we want to keep it
    // around as long as we are running.
    Worker(scoped_refptr<SequencedWorkerPool> worker_pool,
        int thread_number,
        const std::string& thread_name_prefix);
    ~Worker() override;

    // SimpleThread implementation. This actually runs the background thread.
    void Run() override;

    // Gets the worker for the current thread out of thread-local storage.
    static Worker* GetForCurrentThread();

    // Indicates that a task is about to be run. The parameters provide
    // additional metainformation about the task being run.
    void set_running_task_info(SequenceToken token,
        WorkerShutdown shutdown_behavior)
    {
        is_processing_task_ = true;
        task_sequence_token_ = token;
        task_shutdown_behavior_ = shutdown_behavior;
    }

    // Indicates that the task has finished running.
    void reset_running_task_info() { is_processing_task_ = false; }

    // Whether the worker is processing a task.
    bool is_processing_task() { return is_processing_task_; }

    SequenceToken task_sequence_token() const
    {
        DCHECK(is_processing_task_);
        return task_sequence_token_;
    }

    WorkerShutdown task_shutdown_behavior() const
    {
        DCHECK(is_processing_task_);
        return task_shutdown_behavior_;
    }

    scoped_refptr<SequencedWorkerPool> worker_pool() const
    {
        return worker_pool_;
    }

private:
    static LazyInstance<ThreadLocalPointer<SequencedWorkerPool::Worker>>::Leaky
        lazy_tls_ptr_;

    scoped_refptr<SequencedWorkerPool> worker_pool_;
    // The sequence token of the task being processed. Only valid when
    // is_processing_task_ is true.
    SequenceToken task_sequence_token_;
    // The shutdown behavior of the task being processed. Only valid when
    // is_processing_task_ is true.
    WorkerShutdown task_shutdown_behavior_;
    // Whether the Worker is processing a task.
    bool is_processing_task_;

    DISALLOW_COPY_AND_ASSIGN(Worker);
};

// Inner ----------------------------------------------------------------------

class SequencedWorkerPool::Inner {
public:
    // Take a raw pointer to |worker| to avoid cycles (since we're owned
    // by it).
    Inner(SequencedWorkerPool* worker_pool, size_t max_threads,
        const std::string& thread_name_prefix,
        TestingObserver* observer);

    ~Inner();

    static SequenceToken GetSequenceToken();

    SequenceToken GetNamedSequenceToken(const std::string& name);

    // This function accepts a name and an ID. If the name is null, the
    // token ID is used. This allows us to implement the optional name lookup
    // from a single function without having to enter the lock a separate time.
    bool PostTask(const std::string* optional_token_name,
        SequenceToken sequence_token,
        WorkerShutdown shutdown_behavior,
        const tracked_objects::Location& from_here,
        const Closure& task,
        TimeDelta delay);

    bool RunsTasksOnCurrentThread() const;

    bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;

    bool IsRunningSequence(SequenceToken sequence_token) const;

    void SetRunningTaskInfoForCurrentThread(SequenceToken sequence_token,
        WorkerShutdown shutdown_behavior);

    void CleanupForTesting();

    void SignalHasWorkForTesting();

    int GetWorkSignalCountForTesting() const;

    void Shutdown(int max_blocking_tasks_after_shutdown);

    bool IsShutdownInProgress();

    // Runs the worker loop on the background thread.
    void ThreadLoop(Worker* this_worker);

private:
    enum GetWorkStatus {
        GET_WORK_FOUND,
        GET_WORK_NOT_FOUND,
        GET_WORK_WAIT,
    };

    enum CleanupState {
        CLEANUP_REQUESTED,
        CLEANUP_STARTING,
        CLEANUP_RUNNING,
        CLEANUP_FINISHING,
        CLEANUP_DONE,
    };

    // Called from within the lock, this converts the given token name into a
    // token ID, creating a new one if necessary.
    int LockedGetNamedTokenID(const std::string& name);

    // Called from within the lock, this returns the next sequence task number.
    int64_t LockedGetNextSequenceTaskNumber();

    // Gets new task. There are 3 cases depending on the return value:
    //
    // 1) If the return value is |GET_WORK_FOUND|, |task| is filled in and should
    //    be run immediately.
    // 2) If the return value is |GET_WORK_NOT_FOUND|, there are no tasks to run,
    //    and |task| is not filled in. In this case, the caller should wait until
    //    a task is posted.
    // 3) If the return value is |GET_WORK_WAIT|, there are no tasks to run
    //    immediately, and |task| is not filled in. Likewise, |wait_time| is
    //    filled in the time to wait until the next task to run. In this case, the
    //    caller should wait the time.
    //
    // In any case, the calling code should clear the given
    // delete_these_outside_lock vector the next time the lock is released.
    // See the implementation for a more detailed description.
    GetWorkStatus GetWork(SequencedTask* task,
        TimeDelta* wait_time,
        std::vector<Closure>* delete_these_outside_lock);

    void HandleCleanup();

    // Peforms init and cleanup around running the given task. WillRun...
    // returns the value from PrepareToStartAdditionalThreadIfNecessary.
    // The calling code should call FinishStartingAdditionalThread once the
    // lock is released if the return values is nonzero.
    int WillRunWorkerTask(const SequencedTask& task);
    void DidRunWorkerTask(const SequencedTask& task);

    // Returns true if there are no threads currently running the given
    // sequence token.
    bool IsSequenceTokenRunnable(int sequence_token_id) const;

    // Checks if all threads are busy and the addition of one more could run an
    // additional task waiting in the queue. This must be called from within
    // the lock.
    //
    // If another thread is helpful, this will mark the thread as being in the
    // process of starting and returns the index of the new thread which will be
    // 0 or more. The caller should then call FinishStartingAdditionalThread to
    // complete initialization once the lock is released.
    //
    // If another thread is not necessary, returne 0;
    //
    // See the implementedion for more.
    int PrepareToStartAdditionalThreadIfHelpful();

    // The second part of thread creation after
    // PrepareToStartAdditionalThreadIfHelpful with the thread number it
    // generated. This actually creates the thread and should be called outside
    // the lock to avoid blocking important work starting a thread in the lock.
    void FinishStartingAdditionalThread(int thread_number);

    // Signal |has_work_| and increment |has_work_signal_count_|.
    void SignalHasWork();

    // Checks whether there is work left that's blocking shutdown. Must be
    // called inside the lock.
    bool CanShutdown() const;

    SequencedWorkerPool* const worker_pool_;

    // The last sequence number used. Managed by GetSequenceToken, since this
    // only does threadsafe increment operations, you do not need to hold the
    // lock. This is class-static to make SequenceTokens issued by
    // GetSequenceToken unique across SequencedWorkerPool instances.
    static base::StaticAtomicSequenceNumber g_last_sequence_number_;

    // This lock protects |everything in this class|. Do not read or modify
    // anything without holding this lock. Do not block while holding this
    // lock.
    mutable Lock lock_;

    // Condition variable that is waited on by worker threads until new
    // tasks are posted or shutdown starts.
    ConditionVariable has_work_cv_;

    // Condition variable that is waited on by non-worker threads (in
    // Shutdown()) until CanShutdown() goes to true.
    ConditionVariable can_shutdown_cv_;

    // The maximum number of worker threads we'll create.
    const size_t max_threads_;

    const std::string thread_name_prefix_;

    // Associates all known sequence token names with their IDs.
    std::map<std::string, int> named_sequence_tokens_;

    // Owning pointers to all threads we've created so far, indexed by
    // ID. Since we lazily create threads, this may be less than
    // max_threads_ and will be initially empty.
    using ThreadMap = std::map<PlatformThreadId, std::unique_ptr<Worker>>;
    ThreadMap threads_;

    // Set to true when we're in the process of creating another thread.
    // See PrepareToStartAdditionalThreadIfHelpful for more.
    bool thread_being_created_;

    // Number of threads currently waiting for work.
    size_t waiting_thread_count_;

    // Number of threads currently running tasks that have the BLOCK_SHUTDOWN
    // or SKIP_ON_SHUTDOWN flag set.
    size_t blocking_shutdown_thread_count_;

    // A set of all pending tasks in time-to-run order. These are tasks that are
    // either waiting for a thread to run on, waiting for their time to run,
    // or blocked on a previous task in their sequence. We have to iterate over
    // the tasks by time-to-run order, so we use the set instead of the
    // traditional priority_queue.
    typedef std::set<SequencedTask, SequencedTaskLessThan> PendingTaskSet;
    PendingTaskSet pending_tasks_;

    // The next sequence number for a new sequenced task.
    int64_t next_sequence_task_number_;

    // Number of tasks in the pending_tasks_ list that are marked as blocking
    // shutdown.
    size_t blocking_shutdown_pending_task_count_;

    // Lists all sequence tokens currently executing.
    std::set<int> current_sequences_;

    // An ID for each posted task to distinguish the task from others in traces.
    int trace_id_;

    // Set when Shutdown is called and no further tasks should be
    // allowed, though we may still be running existing tasks.
    bool shutdown_called_;

    // The number of new BLOCK_SHUTDOWN tasks that may be posted after Shudown()
    // has been called.
    int max_blocking_tasks_after_shutdown_;

    // State used to cleanup for testing, all guarded by lock_.
    CleanupState cleanup_state_;
    size_t cleanup_idlers_;
    ConditionVariable cleanup_cv_;

    TestingObserver* const testing_observer_;

    DISALLOW_COPY_AND_ASSIGN(Inner);
};

// Worker definitions ---------------------------------------------------------

SequencedWorkerPool::Worker::Worker(
    scoped_refptr<SequencedWorkerPool> worker_pool,
    int thread_number,
    const std::string& prefix)
    : SimpleThread(prefix + StringPrintf("Worker%d", thread_number))
    , worker_pool_(std::move(worker_pool))
    , task_shutdown_behavior_(BLOCK_SHUTDOWN)
    , is_processing_task_(false)
{
    Start();
}

SequencedWorkerPool::Worker::~Worker()
{
}

void SequencedWorkerPool::Worker::Run()
{
#if defined(OS_WIN)
    win::ScopedCOMInitializer com_initializer;
#endif

    // Store a pointer to this worker in thread local storage for static function
    // access.
    DCHECK(!lazy_tls_ptr_.Get().Get());
    lazy_tls_ptr_.Get().Set(this);

    // Just jump back to the Inner object to run the thread, since it has all the
    // tracking information and queues. It might be more natural to implement
    // using DelegateSimpleThread and have Inner implement the Delegate to avoid
    // having these worker objects at all, but that method lacks the ability to
    // send thread-specific information easily to the thread loop.
    worker_pool_->inner_->ThreadLoop(this);
    // Release our cyclic reference once we're done.
    worker_pool_ = nullptr;
}

// static
SequencedWorkerPool::Worker*
SequencedWorkerPool::Worker::GetForCurrentThread()
{
    // Don't construct lazy instance on check.
    if (lazy_tls_ptr_ == nullptr)
        return nullptr;

    return lazy_tls_ptr_.Get().Get();
}

// static
LazyInstance<ThreadLocalPointer<SequencedWorkerPool::Worker>>::Leaky
    SequencedWorkerPool::Worker::lazy_tls_ptr_
    = LAZY_INSTANCE_INITIALIZER;

// Inner definitions ---------------------------------------------------------

SequencedWorkerPool::Inner::Inner(
    SequencedWorkerPool* worker_pool,
    size_t max_threads,
    const std::string& thread_name_prefix,
    TestingObserver* observer)
    : worker_pool_(worker_pool)
    , lock_()
    , has_work_cv_(&lock_)
    , can_shutdown_cv_(&lock_)
    , max_threads_(max_threads)
    , thread_name_prefix_(thread_name_prefix)
    , thread_being_created_(false)
    , waiting_thread_count_(0)
    , blocking_shutdown_thread_count_(0)
    , next_sequence_task_number_(0)
    , blocking_shutdown_pending_task_count_(0)
    , trace_id_(0)
    , shutdown_called_(false)
    , max_blocking_tasks_after_shutdown_(0)
    , cleanup_state_(CLEANUP_DONE)
    , cleanup_idlers_(0)
    , cleanup_cv_(&lock_)
    , testing_observer_(observer)
{
}

SequencedWorkerPool::Inner::~Inner()
{
    // You must call Shutdown() before destroying the pool.
    DCHECK(shutdown_called_);

    // Need to explicitly join with the threads before they're destroyed or else
    // they will be running when our object is half torn down.
    for (ThreadMap::iterator it = threads_.begin(); it != threads_.end(); ++it)
        it->second->Join();
    threads_.clear();

    if (testing_observer_)
        testing_observer_->OnDestruct();
}

// static
SequencedWorkerPool::SequenceToken
SequencedWorkerPool::Inner::GetSequenceToken()
{
    // Need to add one because StaticAtomicSequenceNumber starts at zero, which
    // is used as a sentinel value in SequenceTokens.
    return SequenceToken(g_last_sequence_number_.GetNext() + 1);
}

SequencedWorkerPool::SequenceToken
SequencedWorkerPool::Inner::GetNamedSequenceToken(const std::string& name)
{
    AutoLock lock(lock_);
    return SequenceToken(LockedGetNamedTokenID(name));
}

bool SequencedWorkerPool::Inner::PostTask(
    const std::string* optional_token_name,
    SequenceToken sequence_token,
    WorkerShutdown shutdown_behavior,
    const tracked_objects::Location& from_here,
    const Closure& task,
    TimeDelta delay)
{
    DCHECK(delay.is_zero() || shutdown_behavior == SKIP_ON_SHUTDOWN);
    SequencedTask sequenced(from_here);
    sequenced.sequence_token_id = sequence_token.id_;
    sequenced.shutdown_behavior = shutdown_behavior;
    sequenced.posted_from = from_here;
    sequenced.task = shutdown_behavior == BLOCK_SHUTDOWN ? base::MakeCriticalClosure(task) : task;
    sequenced.time_to_run = TimeTicks::Now() + delay;

    int create_thread_id = 0;
    {
        AutoLock lock(lock_);
        if (shutdown_called_) {
            // Don't allow a new task to be posted if it doesn't block shutdown.
            if (shutdown_behavior != BLOCK_SHUTDOWN)
                return false;

            // If the current thread is running a task, and that task doesn't block
            // shutdown, then it shouldn't be allowed to post any more tasks.
            ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
            if (found != threads_.end() && found->second->is_processing_task() && found->second->task_shutdown_behavior() != BLOCK_SHUTDOWN) {
                return false;
            }

            if (max_blocking_tasks_after_shutdown_ <= 0) {
                DLOG(WARNING) << "BLOCK_SHUTDOWN task disallowed";
                return false;
            }
            max_blocking_tasks_after_shutdown_ -= 1;
        }

        // The trace_id is used for identifying the task in about:tracing.
        sequenced.trace_id = trace_id_++;

        TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
            "SequencedWorkerPool::Inner::PostTask",
            TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))),
            TRACE_EVENT_FLAG_FLOW_OUT);

        sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber();

        // Now that we have the lock, apply the named token rules.
        if (optional_token_name)
            sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);

        pending_tasks_.insert(sequenced);
        if (shutdown_behavior == BLOCK_SHUTDOWN)
            blocking_shutdown_pending_task_count_++;

        create_thread_id = PrepareToStartAdditionalThreadIfHelpful();
    }

    // Actually start the additional thread or signal an existing one now that
    // we're outside the lock.
    if (create_thread_id)
        FinishStartingAdditionalThread(create_thread_id);
    else
        SignalHasWork();

    return true;
}

bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const
{
    AutoLock lock(lock_);
    return ContainsKey(threads_, PlatformThread::CurrentId());
}

bool SequencedWorkerPool::Inner::IsRunningSequenceOnCurrentThread(
    SequenceToken sequence_token) const
{
    AutoLock lock(lock_);
    ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
    if (found == threads_.end())
        return false;
    return found->second->is_processing_task() && sequence_token.Equals(found->second->task_sequence_token());
}

bool SequencedWorkerPool::Inner::IsRunningSequence(
    SequenceToken sequence_token) const
{
    DCHECK(sequence_token.IsValid());
    AutoLock lock(lock_);
    return !IsSequenceTokenRunnable(sequence_token.id_);
}

void SequencedWorkerPool::Inner::SetRunningTaskInfoForCurrentThread(
    SequenceToken sequence_token,
    WorkerShutdown shutdown_behavior)
{
    AutoLock lock(lock_);
    ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
    DCHECK(found != threads_.end());
    DCHECK(found->second->is_processing_task());
    DCHECK(!found->second->task_sequence_token().IsValid());
    found->second->set_running_task_info(sequence_token, shutdown_behavior);

    // Mark the sequence token as in use.
    bool success = current_sequences_.insert(sequence_token.id_).second;
    DCHECK(success);
}

// See https://code.google.com/p/chromium/issues/detail?id=168415
void SequencedWorkerPool::Inner::CleanupForTesting()
{
    DCHECK(!RunsTasksOnCurrentThread());
    base::ThreadRestrictions::ScopedAllowWait allow_wait;
    AutoLock lock(lock_);
    CHECK_EQ(CLEANUP_DONE, cleanup_state_);
    if (shutdown_called_)
        return;
    if (pending_tasks_.empty() && waiting_thread_count_ == threads_.size())
        return;
    cleanup_state_ = CLEANUP_REQUESTED;
    cleanup_idlers_ = 0;
    has_work_cv_.Signal();
    while (cleanup_state_ != CLEANUP_DONE)
        cleanup_cv_.Wait();
}

void SequencedWorkerPool::Inner::SignalHasWorkForTesting()
{
    SignalHasWork();
}

void SequencedWorkerPool::Inner::Shutdown(
    int max_new_blocking_tasks_after_shutdown)
{
    DCHECK_GE(max_new_blocking_tasks_after_shutdown, 0);
    {
        AutoLock lock(lock_);
        // Cleanup and Shutdown should not be called concurrently.
        CHECK_EQ(CLEANUP_DONE, cleanup_state_);
        if (shutdown_called_)
            return;
        shutdown_called_ = true;
        max_blocking_tasks_after_shutdown_ = max_new_blocking_tasks_after_shutdown;

        // Tickle the threads. This will wake up a waiting one so it will know that
        // it can exit, which in turn will wake up any other waiting ones.
        SignalHasWork();

        // There are no pending or running tasks blocking shutdown, we're done.
        if (CanShutdown())
            return;
    }

    // If we're here, then something is blocking shutdown.  So wait for
    // CanShutdown() to go to true.

    if (testing_observer_)
        testing_observer_->WillWaitForShutdown();

#if !defined(OS_NACL)
    TimeTicks shutdown_wait_begin = TimeTicks::Now();
#endif

    {
        base::ThreadRestrictions::ScopedAllowWait allow_wait;
        AutoLock lock(lock_);
        while (!CanShutdown())
            can_shutdown_cv_.Wait();
    }
#if !defined(OS_NACL)
    UMA_HISTOGRAM_TIMES("SequencedWorkerPool.ShutdownDelayTime",
        TimeTicks::Now() - shutdown_wait_begin);
#endif
}

bool SequencedWorkerPool::Inner::IsShutdownInProgress()
{
    AutoLock lock(lock_);
    return shutdown_called_;
}

void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker)
{
    {
        AutoLock lock(lock_);
        DCHECK(thread_being_created_);
        thread_being_created_ = false;
        auto result = threads_.insert(
            std::make_pair(this_worker->tid(), WrapUnique(this_worker)));
        DCHECK(result.second);

        while (true) {
#if defined(OS_MACOSX)
            base::mac::ScopedNSAutoreleasePool autorelease_pool;
#endif

            HandleCleanup();

            // See GetWork for what delete_these_outside_lock is doing.
            SequencedTask task;
            TimeDelta wait_time;
            std::vector<Closure> delete_these_outside_lock;
            GetWorkStatus status = GetWork(&task, &wait_time, &delete_these_outside_lock);
            if (status == GET_WORK_FOUND) {
                TRACE_EVENT_WITH_FLOW2(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
                    "SequencedWorkerPool::Inner::ThreadLoop",
                    TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))),
                    TRACE_EVENT_FLAG_FLOW_IN,
                    "src_file", task.posted_from.file_name(),
                    "src_func", task.posted_from.function_name());
                TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION task_event(
                    task.posted_from.file_name());
                int new_thread_id = WillRunWorkerTask(task);
                {
                    AutoUnlock unlock(lock_);
                    // There may be more work available, so wake up another
                    // worker thread. (Technically not required, since we
                    // already get a signal for each new task, but it doesn't
                    // hurt.)
                    SignalHasWork();
                    delete_these_outside_lock.clear();

                    // Complete thread creation outside the lock if necessary.
                    if (new_thread_id)
                        FinishStartingAdditionalThread(new_thread_id);

                    this_worker->set_running_task_info(
                        SequenceToken(task.sequence_token_id), task.shutdown_behavior);

                    tracked_objects::TaskStopwatch stopwatch;
                    stopwatch.Start();
                    task.task.Run();
                    stopwatch.Stop();

                    tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(
                        task, stopwatch);

                    // Update the sequence token in case it has been set from within the
                    // task, so it can be removed from the set of currently running
                    // sequences in DidRunWorkerTask() below.
                    task.sequence_token_id = this_worker->task_sequence_token().id_;

                    // Make sure our task is erased outside the lock for the
                    // same reason we do this with delete_these_oustide_lock.
                    // Also, do it before calling reset_running_task_info() so
                    // that sequence-checking from within the task's destructor
                    // still works.
                    task.task = Closure();

                    this_worker->reset_running_task_info();
                }
                DidRunWorkerTask(task); // Must be done inside the lock.
            } else if (cleanup_state_ == CLEANUP_RUNNING) {
                switch (status) {
                case GET_WORK_WAIT: {
                    AutoUnlock unlock(lock_);
                    delete_these_outside_lock.clear();
                } break;
                case GET_WORK_NOT_FOUND:
                    CHECK(delete_these_outside_lock.empty());
                    cleanup_state_ = CLEANUP_FINISHING;
                    cleanup_cv_.Broadcast();
                    break;
                default:
                    NOTREACHED();
                }
            } else {
                // When we're terminating and there's no more work, we can
                // shut down, other workers can complete any pending or new tasks.
                // We can get additional tasks posted after shutdown_called_ is set
                // but only worker threads are allowed to post tasks at that time, and
                // the workers responsible for posting those tasks will be available
                // to run them. Also, there may be some tasks stuck behind running
                // ones with the same sequence token, but additional threads won't
                // help this case.
                if (shutdown_called_ && blocking_shutdown_pending_task_count_ == 0) {
                    AutoUnlock unlock(lock_);
                    delete_these_outside_lock.clear();
                    break;
                }

                // No work was found, but there are tasks that need deletion. The
                // deletion must happen outside of the lock.
                if (delete_these_outside_lock.size()) {
                    AutoUnlock unlock(lock_);
                    delete_these_outside_lock.clear();

                    // Since the lock has been released, |status| may no longer be
                    // accurate. It might read GET_WORK_WAIT even if there are tasks
                    // ready to perform work. Jump to the top of the loop to recalculate
                    // |status|.
                    continue;
                }

                waiting_thread_count_++;

                switch (status) {
                case GET_WORK_NOT_FOUND:
                    has_work_cv_.Wait();
                    break;
                case GET_WORK_WAIT:
                    has_work_cv_.TimedWait(wait_time);
                    break;
                default:
                    NOTREACHED();
                }
                waiting_thread_count_--;
            }
        }
    } // Release lock_.

    // We noticed we should exit. Wake up the next worker so it knows it should
    // exit as well (because the Shutdown() code only signals once).
    SignalHasWork();

    // Possibly unblock shutdown.
    can_shutdown_cv_.Signal();
}

void SequencedWorkerPool::Inner::HandleCleanup()
{
    lock_.AssertAcquired();
    if (cleanup_state_ == CLEANUP_DONE)
        return;
    if (cleanup_state_ == CLEANUP_REQUESTED) {
        // We win, we get to do the cleanup as soon as the others wise up and idle.
        cleanup_state_ = CLEANUP_STARTING;
        while (thread_being_created_ || cleanup_idlers_ != threads_.size() - 1) {
            has_work_cv_.Signal();
            cleanup_cv_.Wait();
        }
        cleanup_state_ = CLEANUP_RUNNING;
        return;
    }
    if (cleanup_state_ == CLEANUP_STARTING) {
        // Another worker thread is cleaning up, we idle here until thats done.
        ++cleanup_idlers_;
        cleanup_cv_.Broadcast();
        while (cleanup_state_ != CLEANUP_FINISHING) {
            cleanup_cv_.Wait();
        }
        --cleanup_idlers_;
        cleanup_cv_.Broadcast();
        return;
    }
    if (cleanup_state_ == CLEANUP_FINISHING) {
        // We wait for all idlers to wake up prior to being DONE.
        while (cleanup_idlers_ != 0) {
            cleanup_cv_.Broadcast();
            cleanup_cv_.Wait();
        }
        if (cleanup_state_ == CLEANUP_FINISHING) {
            cleanup_state_ = CLEANUP_DONE;
            cleanup_cv_.Signal();
        }
        return;
    }
}

int SequencedWorkerPool::Inner::LockedGetNamedTokenID(
    const std::string& name)
{
    lock_.AssertAcquired();
    DCHECK(!name.empty());

    std::map<std::string, int>::const_iterator found = named_sequence_tokens_.find(name);
    if (found != named_sequence_tokens_.end())
        return found->second; // Got an existing one.

    // Create a new one for this name.
    SequenceToken result = GetSequenceToken();
    named_sequence_tokens_.insert(std::make_pair(name, result.id_));
    return result.id_;
}

int64_t SequencedWorkerPool::Inner::LockedGetNextSequenceTaskNumber()
{
    lock_.AssertAcquired();
    // We assume that we never create enough tasks to wrap around.
    return next_sequence_task_number_++;
}

SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
    SequencedTask* task,
    TimeDelta* wait_time,
    std::vector<Closure>* delete_these_outside_lock)
{
    lock_.AssertAcquired();

    // Find the next task with a sequence token that's not currently in use.
    // If the token is in use, that means another thread is running something
    // in that sequence, and we can't run it without going out-of-order.
    //
    // This algorithm is simple and fair, but inefficient in some cases. For
    // example, say somebody schedules 1000 slow tasks with the same sequence
    // number. We'll have to go through all those tasks each time we feel like
    // there might be work to schedule. If this proves to be a problem, we
    // should make this more efficient.
    //
    // One possible enhancement would be to keep a map from sequence ID to a
    // list of pending but currently blocked SequencedTasks for that ID.
    // When a worker finishes a task of one sequence token, it can pick up the
    // next one from that token right away.
    //
    // This may lead to starvation if there are sufficient numbers of sequences
    // in use. To alleviate this, we could add an incrementing priority counter
    // to each SequencedTask. Then maintain a priority_queue of all runnable
    // tasks, sorted by priority counter. When a sequenced task is completed
    // we would pop the head element off of that tasks pending list and add it
    // to the priority queue. Then we would run the first item in the priority
    // queue.

    GetWorkStatus status = GET_WORK_NOT_FOUND;
    int unrunnable_tasks = 0;
    PendingTaskSet::iterator i = pending_tasks_.begin();
    // We assume that the loop below doesn't take too long and so we can just do
    // a single call to TimeTicks::Now().
    const TimeTicks current_time = TimeTicks::Now();
    while (i != pending_tasks_.end()) {
        if (!IsSequenceTokenRunnable(i->sequence_token_id)) {
            unrunnable_tasks++;
            ++i;
            continue;
        }

        if (shutdown_called_ && i->shutdown_behavior != BLOCK_SHUTDOWN) {
            // We're shutting down and the task we just found isn't blocking
            // shutdown. Delete it and get more work.
            //
            // Note that we do not want to delete unrunnable tasks. Deleting a task
            // can have side effects (like freeing some objects) and deleting a
            // task that's supposed to run after one that's currently running could
            // cause an obscure crash.
            //
            // We really want to delete these tasks outside the lock in case the
            // closures are holding refs to objects that want to post work from
            // their destructorss (which would deadlock). The closures are
            // internally refcounted, so we just need to keep a copy of them alive
            // until the lock is exited. The calling code can just clear() the
            // vector they passed to us once the lock is exited to make this
            // happen.
            delete_these_outside_lock->push_back(i->task);
            pending_tasks_.erase(i++);
            continue;
        }

        if (i->time_to_run > current_time) {
            // The time to run has not come yet.
            *wait_time = i->time_to_run - current_time;
            status = GET_WORK_WAIT;
            if (cleanup_state_ == CLEANUP_RUNNING) {
                // Deferred tasks are deleted when cleaning up, see Inner::ThreadLoop.
                delete_these_outside_lock->push_back(i->task);
                pending_tasks_.erase(i);
            }
            break;
        }

        // Found a runnable task.
        *task = *i;
        pending_tasks_.erase(i);
        if (task->shutdown_behavior == BLOCK_SHUTDOWN) {
            blocking_shutdown_pending_task_count_--;
        }

        status = GET_WORK_FOUND;
        break;
    }

    return status;
}

int SequencedWorkerPool::Inner::WillRunWorkerTask(const SequencedTask& task)
{
    lock_.AssertAcquired();

    // Mark the task's sequence number as in use.
    if (task.sequence_token_id)
        current_sequences_.insert(task.sequence_token_id);

    // Ensure that threads running tasks posted with either SKIP_ON_SHUTDOWN
    // or BLOCK_SHUTDOWN will prevent shutdown until that task or thread
    // completes.
    if (task.shutdown_behavior != CONTINUE_ON_SHUTDOWN)
        blocking_shutdown_thread_count_++;

    // We just picked up a task. Since StartAdditionalThreadIfHelpful only
    // creates a new thread if there is no free one, there is a race when posting
    // tasks that many tasks could have been posted before a thread started
    // running them, so only one thread would have been created. So we also check
    // whether we should create more threads after removing our task from the
    // queue, which also has the nice side effect of creating the workers from
    // background threads rather than the main thread of the app.
    //
    // If another thread wasn't created, we want to wake up an existing thread
    // if there is one waiting to pick up the next task.
    //
    // Note that we really need to do this *before* running the task, not
    // after. Otherwise, if more than one task is posted, the creation of the
    // second thread (since we only create one at a time) will be blocked by
    // the execution of the first task, which could be arbitrarily long.
    return PrepareToStartAdditionalThreadIfHelpful();
}

void SequencedWorkerPool::Inner::DidRunWorkerTask(const SequencedTask& task)
{
    lock_.AssertAcquired();

    if (task.shutdown_behavior != CONTINUE_ON_SHUTDOWN) {
        DCHECK_GT(blocking_shutdown_thread_count_, 0u);
        blocking_shutdown_thread_count_--;
    }

    if (task.sequence_token_id)
        current_sequences_.erase(task.sequence_token_id);
}

bool SequencedWorkerPool::Inner::IsSequenceTokenRunnable(
    int sequence_token_id) const
{
    lock_.AssertAcquired();
    return !sequence_token_id || current_sequences_.find(sequence_token_id) == current_sequences_.end();
}

int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful()
{
    lock_.AssertAcquired();
    // How thread creation works:
    //
    // We'de like to avoid creating threads with the lock held. However, we
    // need to be sure that we have an accurate accounting of the threads for
    // proper Joining and deltion on shutdown.
    //
    // We need to figure out if we need another thread with the lock held, which
    // is what this function does. It then marks us as in the process of creating
    // a thread. When we do shutdown, we wait until the thread_being_created_
    // flag is cleared, which ensures that the new thread is properly added to
    // all the data structures and we can't leak it. Once shutdown starts, we'll
    // refuse to create more threads or they would be leaked.
    //
    // Note that this creates a mostly benign race condition on shutdown that
    // will cause fewer workers to be created than one would expect. It isn't
    // much of an issue in real life, but affects some tests. Since we only spawn
    // one worker at a time, the following sequence of events can happen:
    //
    //  1. Main thread posts a bunch of unrelated tasks that would normally be
    //     run on separate threads.
    //  2. The first task post causes us to start a worker. Other tasks do not
    //     cause a worker to start since one is pending.
    //  3. Main thread initiates shutdown.
    //  4. No more threads are created since the shutdown_called_ flag is set.
    //
    // The result is that one may expect that max_threads_ workers to be created
    // given the workload, but in reality fewer may be created because the
    // sequence of thread creation on the background threads is racing with the
    // shutdown call.
    if (!shutdown_called_ && !thread_being_created_ && cleanup_state_ == CLEANUP_DONE && threads_.size() < max_threads_ && waiting_thread_count_ == 0) {
        // We could use an additional thread if there's work to be done.
        for (PendingTaskSet::const_iterator i = pending_tasks_.begin();
             i != pending_tasks_.end(); ++i) {
            if (IsSequenceTokenRunnable(i->sequence_token_id)) {
                // Found a runnable task, mark the thread as being started.
                thread_being_created_ = true;
                return static_cast<int>(threads_.size() + 1);
            }
        }
    }
    return 0;
}

void SequencedWorkerPool::Inner::FinishStartingAdditionalThread(
    int thread_number)
{
    // Called outside of the lock.
    DCHECK_GT(thread_number, 0);

    // The worker is assigned to the list when the thread actually starts, which
    // will manage the memory of the pointer.
    new Worker(worker_pool_, thread_number, thread_name_prefix_);
}

void SequencedWorkerPool::Inner::SignalHasWork()
{
    has_work_cv_.Signal();
    if (testing_observer_) {
        testing_observer_->OnHasWork();
    }
}

bool SequencedWorkerPool::Inner::CanShutdown() const
{
    lock_.AssertAcquired();
    // See PrepareToStartAdditionalThreadIfHelpful for how thread creation works.
    return !thread_being_created_ && blocking_shutdown_thread_count_ == 0 && blocking_shutdown_pending_task_count_ == 0;
}

base::StaticAtomicSequenceNumber
    SequencedWorkerPool::Inner::g_last_sequence_number_;

// SequencedWorkerPool --------------------------------------------------------

std::string SequencedWorkerPool::SequenceToken::ToString() const
{
    return base::StringPrintf("[%d]", id_);
}

// static
SequencedWorkerPool::SequenceToken
SequencedWorkerPool::GetSequenceTokenForCurrentThread()
{
    Worker* worker = Worker::GetForCurrentThread();
    if (!worker)
        return SequenceToken();

    return worker->task_sequence_token();
}

// static
scoped_refptr<SequencedWorkerPool>
SequencedWorkerPool::GetWorkerPoolForCurrentThread()
{
    Worker* worker = Worker::GetForCurrentThread();
    if (!worker)
        return nullptr;

    return worker->worker_pool();
}

// static
scoped_refptr<SequencedTaskRunner>
SequencedWorkerPool::GetSequencedTaskRunnerForCurrentThread()
{
    Worker* worker = Worker::GetForCurrentThread();

    // If there is no worker, this thread is not a worker thread. Otherwise, it is
    // currently running a task (sequenced or unsequenced).
    if (!worker)
        return nullptr;

    scoped_refptr<SequencedWorkerPool> pool = worker->worker_pool();
    SequenceToken sequence_token = worker->task_sequence_token();
    WorkerShutdown shutdown_behavior = worker->task_shutdown_behavior();
    if (!sequence_token.IsValid()) {
        // Create a new sequence token and bind this thread to it, to make sure that
        // a task posted to the SequencedTaskRunner we are going to return is not
        // immediately going to run on a different thread.
        sequence_token = Inner::GetSequenceToken();
        pool->inner_->SetRunningTaskInfoForCurrentThread(sequence_token,
            shutdown_behavior);
    }

    DCHECK(pool->IsRunningSequenceOnCurrentThread(sequence_token));
    return new SequencedWorkerPoolSequencedTaskRunner(
        std::move(pool), sequence_token, shutdown_behavior);
}

SequencedWorkerPool::SequencedWorkerPool(size_t max_threads,
    const std::string& thread_name_prefix)
    : constructor_task_runner_(ThreadTaskRunnerHandle::Get())
    , inner_(new Inner(this, max_threads, thread_name_prefix, NULL))
{
}

SequencedWorkerPool::SequencedWorkerPool(size_t max_threads,
    const std::string& thread_name_prefix,
    TestingObserver* observer)
    : constructor_task_runner_(ThreadTaskRunnerHandle::Get())
    , inner_(new Inner(this, max_threads, thread_name_prefix, observer))
{
}

SequencedWorkerPool::~SequencedWorkerPool() { }

void SequencedWorkerPool::OnDestruct() const
{
    // Avoid deleting ourselves on a worker thread (which would deadlock).
    if (RunsTasksOnCurrentThread()) {
        constructor_task_runner_->DeleteSoon(FROM_HERE, this);
    } else {
        delete this;
    }
}

// static
SequencedWorkerPool::SequenceToken SequencedWorkerPool::GetSequenceToken()
{
    return Inner::GetSequenceToken();
}

SequencedWorkerPool::SequenceToken SequencedWorkerPool::GetNamedSequenceToken(
    const std::string& name)
{
    return inner_->GetNamedSequenceToken(name);
}

scoped_refptr<SequencedTaskRunner> SequencedWorkerPool::GetSequencedTaskRunner(
    SequenceToken token)
{
    return GetSequencedTaskRunnerWithShutdownBehavior(token, BLOCK_SHUTDOWN);
}

scoped_refptr<SequencedTaskRunner>
SequencedWorkerPool::GetSequencedTaskRunnerWithShutdownBehavior(
    SequenceToken token, WorkerShutdown shutdown_behavior)
{
    return new SequencedWorkerPoolSequencedTaskRunner(
        this, token, shutdown_behavior);
}

scoped_refptr<TaskRunner>
SequencedWorkerPool::GetTaskRunnerWithShutdownBehavior(
    WorkerShutdown shutdown_behavior)
{
    return new SequencedWorkerPoolTaskRunner(this, shutdown_behavior);
}

bool SequencedWorkerPool::PostWorkerTask(
    const tracked_objects::Location& from_here,
    const Closure& task)
{
    return inner_->PostTask(NULL, SequenceToken(), BLOCK_SHUTDOWN,
        from_here, task, TimeDelta());
}

bool SequencedWorkerPool::PostDelayedWorkerTask(
    const tracked_objects::Location& from_here,
    const Closure& task,
    TimeDelta delay)
{
    WorkerShutdown shutdown_behavior = delay.is_zero() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
    return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
        from_here, task, delay);
}

bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
    const tracked_objects::Location& from_here,
    const Closure& task,
    WorkerShutdown shutdown_behavior)
{
    return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
        from_here, task, TimeDelta());
}

bool SequencedWorkerPool::PostSequencedWorkerTask(
    SequenceToken sequence_token,
    const tracked_objects::Location& from_here,
    const Closure& task)
{
    return inner_->PostTask(NULL, sequence_token, BLOCK_SHUTDOWN,
        from_here, task, TimeDelta());
}

bool SequencedWorkerPool::PostDelayedSequencedWorkerTask(
    SequenceToken sequence_token,
    const tracked_objects::Location& from_here,
    const Closure& task,
    TimeDelta delay)
{
    WorkerShutdown shutdown_behavior = delay.is_zero() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
    return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
        from_here, task, delay);
}

bool SequencedWorkerPool::PostNamedSequencedWorkerTask(
    const std::string& token_name,
    const tracked_objects::Location& from_here,
    const Closure& task)
{
    DCHECK(!token_name.empty());
    return inner_->PostTask(&token_name, SequenceToken(), BLOCK_SHUTDOWN,
        from_here, task, TimeDelta());
}

bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior(
    SequenceToken sequence_token,
    const tracked_objects::Location& from_here,
    const Closure& task,
    WorkerShutdown shutdown_behavior)
{
    return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
        from_here, task, TimeDelta());
}

bool SequencedWorkerPool::PostDelayedTask(
    const tracked_objects::Location& from_here,
    const Closure& task,
    TimeDelta delay)
{
    return PostDelayedWorkerTask(from_here, task, delay);
}

bool SequencedWorkerPool::RunsTasksOnCurrentThread() const
{
    return inner_->RunsTasksOnCurrentThread();
}

bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread(
    SequenceToken sequence_token) const
{
    return inner_->IsRunningSequenceOnCurrentThread(sequence_token);
}

bool SequencedWorkerPool::IsRunningSequence(
    SequenceToken sequence_token) const
{
    return inner_->IsRunningSequence(sequence_token);
}

void SequencedWorkerPool::FlushForTesting()
{
    inner_->CleanupForTesting();
}

void SequencedWorkerPool::SignalHasWorkForTesting()
{
    inner_->SignalHasWorkForTesting();
}

void SequencedWorkerPool::Shutdown(int max_new_blocking_tasks_after_shutdown)
{
    DCHECK(constructor_task_runner_->BelongsToCurrentThread());
    inner_->Shutdown(max_new_blocking_tasks_after_shutdown);
}

bool SequencedWorkerPool::IsShutdownInProgress()
{
    return inner_->IsShutdownInProgress();
}

} // namespace base
